package org.sag.mallbf;

import java.io.DataOutputStream;

public class Utils {

    public static void putDouble(DataOutputStream dos, double d, int endian) throws Exception {
        long l = Double.doubleToLongBits(d);

        putLong(dos, l, endian);
    }

    public static void putLong(DataOutputStream dos, long l, int endian) throws Exception {
        if (endian == 1) {
            //little
            putByte(dos, l);
            putByte(dos, l >>>  8);
            putByte(dos, l >>> 16);
            putByte(dos, l >>> 24);
            putByte(dos, l >>> 32);
            putByte(dos, l >>> 40);
            putByte(dos, l >>> 48);
            putByte(dos, l >>> 56);
        } else {
            putByte(dos, l >>> 56);
            putByte(dos, l >>> 48);
            putByte(dos, l >>> 40);
            putByte(dos, l >>> 32);
            putByte(dos, l >>> 24);
            putByte(dos, l >>> 16);
            putByte(dos, l >>>  8);
            putByte(dos, l);
        }
    }

    public static void putFloat(DataOutputStream dos, float f, int endian) throws Exception {
        int i = Float.floatToIntBits(f);

        putInt(dos, i, endian);
    }

    public static void putHalfFloat(DataOutputStream dos, float f, int endian) throws Exception {
        int sig = f > 0 ? 0 : 1;

        float abs = Math.abs(f);

        if (abs < 1E-14) {
            //down to 0
            putByte(dos, 0);
            putByte(dos, 0);
        } else if (abs > 1E14) {
            //up to infinity
            if (endian == 1) {
                putByte(dos, 0);
                putByte(dos, sig == 0 ? 0x7C : 0xFC);
            } else {
                putByte(dos, sig == 0 ? 0x7C : 0xFC);
                putByte(dos, 0);
            }
        } else {
            int val = fromFloat(f);

            putShort(dos, (short) val, endian);
        }
    }

    public static void putInt(DataOutputStream dos, int i, int endian) throws Exception {
        if (endian == 1) {
            //little
            putByte(dos, i);
            putByte(dos, i >>>  8);
            putByte(dos, i >>> 16);
            putByte(dos, i >>> 24);
        } else {
            putByte(dos, i >>> 24);
            putByte(dos, i >>> 16);
            putByte(dos, i >>>  8);
            putByte(dos, i);
        }
    }

    public static void putShort(DataOutputStream dos, short s, int endian) throws Exception {
        if (endian == 1) {
            //little
            putByte(dos, s);
            putByte(dos, s >>>  8);
        } else {
            putByte(dos, s >>>  8);
            putByte(dos, s);
        }
    }

    public static void putByte(DataOutputStream dos, byte i) throws Exception {
        dos.writeByte(i);
    }

    public static void putByte(DataOutputStream dos, int i) throws Exception {
        dos.writeByte((i & 0xFF));
    }

    public static void putByte(DataOutputStream dos, long i) throws Exception {
        dos.writeByte((byte) (i & 0xFF));
    }

    public static int fromFloat(float fval) {
        int fbits = Float.floatToIntBits( fval );
        int sign = fbits >>> 16 & 0x8000;          // sign only
        int val = ( fbits & 0x7fffffff ) + 0x1000; // rounded value

        if( val >= 0x47800000 )               // might be or become NaN/Inf
        {                                     // avoid Inf due to rounding
            if( ( fbits & 0x7fffffff ) >= 0x47800000 )
            {                                 // is or must become NaN/Inf
                if( val < 0x7f800000 )        // was value but too large
                    return sign | 0x7c00;     // make it +/-Inf
                return sign | 0x7c00 |        // remains +/-Inf or NaN
                        ( fbits & 0x007fffff ) >>> 13; // keep NaN (and Inf) bits
            }
            return sign | 0x7bff;             // unrounded not quite Inf
        }
        if( val >= 0x38800000 )               // remains normalized value
            return sign | val - 0x38000000 >>> 13; // exp - 127 + 15
        if( val < 0x33000000 )                // too small for subnormal
            return sign;                      // becomes +/-0
        val = ( fbits & 0x7fffffff ) >>> 23;  // tmp exp for subnormal calc
        return sign | ( ( fbits & 0x7fffff | 0x800000 ) // add subnormal bit
                + ( 0x800000 >>> val - 102 )     // round depending on cut off
                >>> 126 - val );   // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
    }
}
